oagi-core 0.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. oagi/__init__.py +148 -0
  2. oagi/agent/__init__.py +33 -0
  3. oagi/agent/default.py +124 -0
  4. oagi/agent/factories.py +74 -0
  5. oagi/agent/observer/__init__.py +38 -0
  6. oagi/agent/observer/agent_observer.py +99 -0
  7. oagi/agent/observer/events.py +28 -0
  8. oagi/agent/observer/exporters.py +445 -0
  9. oagi/agent/observer/protocol.py +12 -0
  10. oagi/agent/protocol.py +55 -0
  11. oagi/agent/registry.py +155 -0
  12. oagi/agent/tasker/__init__.py +33 -0
  13. oagi/agent/tasker/memory.py +160 -0
  14. oagi/agent/tasker/models.py +77 -0
  15. oagi/agent/tasker/planner.py +408 -0
  16. oagi/agent/tasker/taskee_agent.py +512 -0
  17. oagi/agent/tasker/tasker_agent.py +324 -0
  18. oagi/cli/__init__.py +11 -0
  19. oagi/cli/agent.py +281 -0
  20. oagi/cli/display.py +56 -0
  21. oagi/cli/main.py +77 -0
  22. oagi/cli/server.py +94 -0
  23. oagi/cli/tracking.py +55 -0
  24. oagi/cli/utils.py +89 -0
  25. oagi/client/__init__.py +12 -0
  26. oagi/client/async_.py +290 -0
  27. oagi/client/base.py +457 -0
  28. oagi/client/sync.py +293 -0
  29. oagi/exceptions.py +118 -0
  30. oagi/handler/__init__.py +24 -0
  31. oagi/handler/_macos.py +55 -0
  32. oagi/handler/async_pyautogui_action_handler.py +44 -0
  33. oagi/handler/async_screenshot_maker.py +47 -0
  34. oagi/handler/pil_image.py +102 -0
  35. oagi/handler/pyautogui_action_handler.py +291 -0
  36. oagi/handler/screenshot_maker.py +41 -0
  37. oagi/logging.py +55 -0
  38. oagi/server/__init__.py +13 -0
  39. oagi/server/agent_wrappers.py +98 -0
  40. oagi/server/config.py +46 -0
  41. oagi/server/main.py +157 -0
  42. oagi/server/models.py +98 -0
  43. oagi/server/session_store.py +116 -0
  44. oagi/server/socketio_server.py +405 -0
  45. oagi/task/__init__.py +21 -0
  46. oagi/task/async_.py +101 -0
  47. oagi/task/async_short.py +76 -0
  48. oagi/task/base.py +157 -0
  49. oagi/task/short.py +76 -0
  50. oagi/task/sync.py +99 -0
  51. oagi/types/__init__.py +50 -0
  52. oagi/types/action_handler.py +30 -0
  53. oagi/types/async_action_handler.py +30 -0
  54. oagi/types/async_image_provider.py +38 -0
  55. oagi/types/image.py +17 -0
  56. oagi/types/image_provider.py +35 -0
  57. oagi/types/models/__init__.py +32 -0
  58. oagi/types/models/action.py +33 -0
  59. oagi/types/models/client.py +68 -0
  60. oagi/types/models/image_config.py +47 -0
  61. oagi/types/models/step.py +17 -0
  62. oagi/types/step_observer.py +93 -0
  63. oagi/types/url.py +3 -0
  64. oagi_core-0.10.1.dist-info/METADATA +245 -0
  65. oagi_core-0.10.1.dist-info/RECORD +68 -0
  66. oagi_core-0.10.1.dist-info/WHEEL +4 -0
  67. oagi_core-0.10.1.dist-info/entry_points.txt +2 -0
  68. oagi_core-0.10.1.dist-info/licenses/LICENSE +21 -0
oagi/__init__.py ADDED
@@ -0,0 +1,148 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+ import importlib
9
+ from typing import TYPE_CHECKING
10
+
11
+ from oagi.client import AsyncClient, SyncClient
12
+ from oagi.exceptions import (
13
+ APIError,
14
+ AuthenticationError,
15
+ ConfigurationError,
16
+ NetworkError,
17
+ NotFoundError,
18
+ OAGIError,
19
+ RateLimitError,
20
+ RequestTimeoutError,
21
+ ServerError,
22
+ ValidationError,
23
+ check_optional_dependency,
24
+ )
25
+ from oagi.task import Actor, AsyncActor, AsyncShortTask, AsyncTask, ShortTask, Task
26
+ from oagi.types import ImageConfig
27
+ from oagi.types.models import (
28
+ ErrorDetail,
29
+ ErrorResponse,
30
+ GenerateResponse,
31
+ LLMResponse,
32
+ UploadFileResponse,
33
+ )
34
+
35
+ # Lazy imports for optional dependency modules
36
+ # Format: name -> (module_path, package_to_check, extra_name)
37
+ # package_to_check is None if no optional dependency is required
38
+ _LAZY_IMPORTS_DATA: dict[str, tuple[str, str | None, str | None]] = {
39
+ # Desktop handlers (require pyautogui/PIL)
40
+ "AsyncPyautoguiActionHandler": (
41
+ "oagi.handler.async_pyautogui_action_handler",
42
+ "pyautogui",
43
+ "desktop",
44
+ ),
45
+ "AsyncScreenshotMaker": ("oagi.handler.async_screenshot_maker", "PIL", "desktop"),
46
+ "PILImage": ("oagi.handler.pil_image", "PIL", "desktop"),
47
+ "PyautoguiActionHandler": (
48
+ "oagi.handler.pyautogui_action_handler",
49
+ "pyautogui",
50
+ "desktop",
51
+ ),
52
+ "PyautoguiConfig": (
53
+ "oagi.handler.pyautogui_action_handler",
54
+ "pyautogui",
55
+ "desktop",
56
+ ),
57
+ "ScreenshotMaker": ("oagi.handler.screenshot_maker", "PIL", "desktop"),
58
+ # Agent modules (lazy to avoid circular imports)
59
+ "AsyncDefaultAgent": ("oagi.agent.default", None, None),
60
+ "TaskerAgent": ("oagi.agent.tasker", None, None),
61
+ "AsyncAgentObserver": ("oagi.agent.observer.agent_observer", None, None),
62
+ # Server modules (require server dependencies)
63
+ "create_app": ("oagi.server.main", "socketio", "server"),
64
+ "ServerConfig": ("oagi.server.config", "pydantic_settings", "server"),
65
+ "sio": ("oagi.server.socketio_server", "socketio", "server"),
66
+ }
67
+
68
+ if TYPE_CHECKING:
69
+ from oagi.agent.default import AsyncDefaultAgent
70
+ from oagi.agent.observer.agent_observer import AsyncAgentObserver
71
+ from oagi.agent.tasker import TaskerAgent
72
+ from oagi.handler.async_pyautogui_action_handler import AsyncPyautoguiActionHandler
73
+ from oagi.handler.async_screenshot_maker import AsyncScreenshotMaker
74
+ from oagi.handler.pil_image import PILImage
75
+ from oagi.handler.pyautogui_action_handler import (
76
+ PyautoguiActionHandler,
77
+ PyautoguiConfig,
78
+ )
79
+ from oagi.handler.screenshot_maker import ScreenshotMaker
80
+ from oagi.server.config import ServerConfig
81
+ from oagi.server.main import create_app
82
+ from oagi.server.socketio_server import sio
83
+
84
+
85
+ def __getattr__(name: str):
86
+ """Lazy import for optional dependency modules with helpful error messages."""
87
+ if name in _LAZY_IMPORTS_DATA:
88
+ module_path, package, extra = _LAZY_IMPORTS_DATA[name]
89
+ if package is not None:
90
+ check_optional_dependency(package, name, extra)
91
+ module = importlib.import_module(module_path)
92
+ return getattr(module, name)
93
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
94
+
95
+
96
+ def __dir__() -> list[str]:
97
+ """Return all public names including lazy imports."""
98
+ return sorted(set(globals().keys()) | set(_LAZY_IMPORTS_DATA.keys()))
99
+
100
+
101
+ __all__ = [
102
+ # Core sync classes
103
+ "Actor",
104
+ "AsyncActor",
105
+ "Task", # Deprecated: Use Actor instead
106
+ "ShortTask", # Deprecated
107
+ "SyncClient",
108
+ # Core async classes
109
+ "AsyncTask", # Deprecated: Use AsyncActor instead
110
+ "AsyncShortTask", # Deprecated
111
+ "AsyncClient",
112
+ # Agent classes
113
+ "AsyncDefaultAgent",
114
+ "TaskerAgent",
115
+ "AsyncAgentObserver",
116
+ # Configuration
117
+ "ImageConfig",
118
+ # Response models
119
+ "LLMResponse",
120
+ "GenerateResponse",
121
+ "UploadFileResponse",
122
+ "ErrorResponse",
123
+ "ErrorDetail",
124
+ # Exceptions
125
+ "OAGIError",
126
+ "APIError",
127
+ "AuthenticationError",
128
+ "ConfigurationError",
129
+ "NetworkError",
130
+ "NotFoundError",
131
+ "RateLimitError",
132
+ "ServerError",
133
+ "RequestTimeoutError",
134
+ "ValidationError",
135
+ # Lazy imports - Image classes
136
+ "PILImage",
137
+ # Lazy imports - Handler classes
138
+ "PyautoguiActionHandler",
139
+ "PyautoguiConfig",
140
+ "ScreenshotMaker",
141
+ # Lazy imports - Async handler classes
142
+ "AsyncPyautoguiActionHandler",
143
+ "AsyncScreenshotMaker",
144
+ # Lazy imports - Server modules (optional)
145
+ "create_app",
146
+ "ServerConfig",
147
+ "sio",
148
+ ]
oagi/agent/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # Import factories to trigger registration
10
+ from . import factories # noqa: F401
11
+ from .default import AsyncDefaultAgent
12
+ from .observer import AsyncAgentObserver
13
+ from .protocol import Agent, AsyncAgent
14
+ from .registry import (
15
+ async_agent_register,
16
+ create_agent,
17
+ get_agent_factory,
18
+ list_agent_modes,
19
+ )
20
+ from .tasker import TaskeeAgent, TaskerAgent
21
+
22
+ __all__ = [
23
+ "Agent",
24
+ "AsyncAgent",
25
+ "AsyncAgentObserver",
26
+ "AsyncDefaultAgent",
27
+ "TaskerAgent",
28
+ "TaskeeAgent",
29
+ "async_agent_register",
30
+ "create_agent",
31
+ "get_agent_factory",
32
+ "list_agent_modes",
33
+ ]
oagi/agent/default.py ADDED
@@ -0,0 +1,124 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ import logging
10
+
11
+ from .. import AsyncActor
12
+ from ..types import (
13
+ ActionEvent,
14
+ AsyncActionHandler,
15
+ AsyncImageProvider,
16
+ AsyncObserver,
17
+ Image,
18
+ StepEvent,
19
+ )
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def _serialize_image(image: Image | str) -> bytes | str:
25
+ """Convert an image to bytes or keep URL as string."""
26
+ if isinstance(image, str):
27
+ return image
28
+ return image.read()
29
+
30
+
31
+ class AsyncDefaultAgent:
32
+ """Default asynchronous agent implementation using OAGI client."""
33
+
34
+ def __init__(
35
+ self,
36
+ api_key: str | None = None,
37
+ base_url: str | None = None,
38
+ model: str = "lux-actor-1",
39
+ max_steps: int = 20,
40
+ temperature: float | None = 0.5,
41
+ step_observer: AsyncObserver | None = None,
42
+ ):
43
+ self.api_key = api_key
44
+ self.base_url = base_url
45
+ self.model = model
46
+ self.max_steps = max_steps
47
+ self.temperature = temperature
48
+ self.step_observer = step_observer
49
+
50
+ async def execute(
51
+ self,
52
+ instruction: str,
53
+ action_handler: AsyncActionHandler,
54
+ image_provider: AsyncImageProvider,
55
+ ) -> bool:
56
+ async with AsyncActor(
57
+ api_key=self.api_key, base_url=self.base_url, model=self.model
58
+ ) as self.actor:
59
+ logger.info(f"Starting async task execution: {instruction}")
60
+ await self.actor.init_task(instruction, max_steps=self.max_steps)
61
+
62
+ for i in range(self.max_steps):
63
+ step_num = i + 1
64
+ logger.debug(f"Executing step {step_num}/{self.max_steps}")
65
+
66
+ # Capture current state
67
+ image = await image_provider()
68
+
69
+ # Get next step from OAGI
70
+ step = await self.actor.step(image, temperature=self.temperature)
71
+
72
+ # Log reasoning
73
+ if step.reason:
74
+ logger.info(f"Step {step_num}: {step.reason}")
75
+
76
+ # Emit step event
77
+ if self.step_observer:
78
+ await self.step_observer.on_event(
79
+ StepEvent(
80
+ step_num=step_num,
81
+ image=_serialize_image(image),
82
+ step=step,
83
+ )
84
+ )
85
+
86
+ # Execute actions if any
87
+ if step.actions:
88
+ logger.info(f"Actions ({len(step.actions)}):")
89
+ for action in step.actions:
90
+ count_suffix = (
91
+ f" x{action.count}"
92
+ if action.count and action.count > 1
93
+ else ""
94
+ )
95
+ logger.info(
96
+ f" [{action.type.value}] {action.argument}{count_suffix}"
97
+ )
98
+
99
+ error = None
100
+ try:
101
+ await action_handler(step.actions)
102
+ except Exception as e:
103
+ error = str(e)
104
+ raise
105
+
106
+ # Emit action event
107
+ if self.step_observer:
108
+ await self.step_observer.on_event(
109
+ ActionEvent(
110
+ step_num=step_num,
111
+ actions=step.actions,
112
+ error=error,
113
+ )
114
+ )
115
+
116
+ # Check if task is complete
117
+ if step.stop:
118
+ logger.info(f"Task completed successfully after {step_num} steps")
119
+ return True
120
+
121
+ logger.warning(
122
+ f"Task reached max steps ({self.max_steps}) without completion"
123
+ )
124
+ return False
@@ -0,0 +1,74 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+ from oagi.agent.tasker import TaskerAgent
9
+ from oagi.types import AsyncStepObserver
10
+
11
+ from .default import AsyncDefaultAgent
12
+ from .protocol import AsyncAgent
13
+ from .registry import async_agent_register
14
+
15
+
16
+ @async_agent_register(mode="actor")
17
+ def create_default_agent(
18
+ api_key: str | None = None,
19
+ base_url: str | None = None,
20
+ model: str = "lux-actor-1",
21
+ max_steps: int = 20,
22
+ temperature: float = 0.1,
23
+ step_observer: AsyncStepObserver | None = None,
24
+ ) -> AsyncAgent:
25
+ return AsyncDefaultAgent(
26
+ api_key=api_key,
27
+ base_url=base_url,
28
+ model=model,
29
+ max_steps=max_steps,
30
+ temperature=temperature,
31
+ step_observer=step_observer,
32
+ )
33
+
34
+
35
+ @async_agent_register(mode="thinker")
36
+ def create_thinker_agent(
37
+ api_key: str | None = None,
38
+ base_url: str | None = None,
39
+ model: str = "lux-thinker-1",
40
+ max_steps: int = 100,
41
+ temperature: float = 0.1,
42
+ step_observer: AsyncStepObserver | None = None,
43
+ ) -> AsyncAgent:
44
+ return AsyncDefaultAgent(
45
+ api_key=api_key,
46
+ base_url=base_url,
47
+ model=model,
48
+ max_steps=max_steps,
49
+ temperature=temperature,
50
+ step_observer=step_observer,
51
+ )
52
+
53
+
54
+ @async_agent_register(mode="tasker")
55
+ def create_planner_agent(
56
+ api_key: str | None = None,
57
+ base_url: str | None = None,
58
+ model: str = "lux-actor-1",
59
+ max_steps: int = 30,
60
+ temperature: float = 0.1,
61
+ reflection_interval: int = 20,
62
+ step_observer: AsyncStepObserver | None = None,
63
+ ) -> AsyncAgent:
64
+ tasker = TaskerAgent(
65
+ api_key=api_key,
66
+ base_url=base_url,
67
+ model=model,
68
+ max_steps=max_steps,
69
+ temperature=temperature,
70
+ reflection_interval=reflection_interval,
71
+ step_observer=step_observer,
72
+ )
73
+ # tasker.set_task()
74
+ return tasker
@@ -0,0 +1,38 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ from ...types import (
10
+ ActionEvent,
11
+ AsyncObserver,
12
+ BaseEvent,
13
+ ImageEvent,
14
+ LogEvent,
15
+ ObserverEvent,
16
+ PlanEvent,
17
+ SplitEvent,
18
+ StepEvent,
19
+ )
20
+ from .agent_observer import AsyncAgentObserver, ExportFormat
21
+ from .exporters import export_to_html, export_to_json, export_to_markdown
22
+
23
+ __all__ = [
24
+ "ActionEvent",
25
+ "AsyncAgentObserver",
26
+ "AsyncObserver",
27
+ "BaseEvent",
28
+ "ExportFormat",
29
+ "ImageEvent",
30
+ "LogEvent",
31
+ "ObserverEvent",
32
+ "PlanEvent",
33
+ "SplitEvent",
34
+ "StepEvent",
35
+ "export_to_html",
36
+ "export_to_json",
37
+ "export_to_markdown",
38
+ ]
@@ -0,0 +1,99 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ from enum import Enum
10
+
11
+ from ...types import LogEvent, ObserverEvent, SplitEvent
12
+ from .exporters import export_to_html, export_to_json, export_to_markdown
13
+
14
+
15
+ class ExportFormat(str, Enum):
16
+ """Supported export formats."""
17
+
18
+ MARKDOWN = "markdown"
19
+ HTML = "html"
20
+ JSON = "json"
21
+
22
+
23
+ class AsyncAgentObserver:
24
+ """Records agent execution events and exports to various formats.
25
+
26
+ This class implements the AsyncObserver protocol and provides
27
+ functionality for recording events during agent execution and
28
+ exporting them to Markdown or HTML formats.
29
+ """
30
+
31
+ def __init__(self) -> None:
32
+ self.events: list[ObserverEvent] = []
33
+
34
+ async def on_event(self, event: ObserverEvent) -> None:
35
+ """Record an event.
36
+
37
+ Args:
38
+ event: The event to record.
39
+ """
40
+ self.events.append(event)
41
+
42
+ def add_log(self, message: str) -> None:
43
+ """Add a custom log message.
44
+
45
+ Args:
46
+ message: The log message to add.
47
+ """
48
+ self.events.append(LogEvent(message=message))
49
+
50
+ def add_split(self, label: str = "") -> None:
51
+ """Add a visual separator.
52
+
53
+ Args:
54
+ label: Optional label for the separator.
55
+ """
56
+ self.events.append(SplitEvent(label=label))
57
+
58
+ def clear(self) -> None:
59
+ """Clear all recorded events."""
60
+ self.events.clear()
61
+
62
+ def get_events_by_step(self, step_num: int) -> list[ObserverEvent]:
63
+ """Get all events for a specific step.
64
+
65
+ Args:
66
+ step_num: The step number to filter by.
67
+
68
+ Returns:
69
+ List of events for the specified step.
70
+ """
71
+ return [
72
+ event
73
+ for event in self.events
74
+ if hasattr(event, "step_num") and event.step_num == step_num
75
+ ]
76
+
77
+ def export(
78
+ self,
79
+ format: ExportFormat | str,
80
+ path: str,
81
+ images_dir: str | None = None,
82
+ ) -> None:
83
+ """Export recorded events to a file.
84
+
85
+ Args:
86
+ format: Export format (markdown, html, json)
87
+ path: Path to the output file.
88
+ images_dir: Directory to save images (markdown only).
89
+ """
90
+ if isinstance(format, str):
91
+ format = ExportFormat(format.lower())
92
+
93
+ match format:
94
+ case ExportFormat.MARKDOWN:
95
+ export_to_markdown(self.events, path, images_dir)
96
+ case ExportFormat.HTML:
97
+ export_to_html(self.events, path)
98
+ case ExportFormat.JSON:
99
+ export_to_json(self.events, path)
@@ -0,0 +1,28 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # Re-export from types for convenience
10
+ from ...types import (
11
+ ActionEvent,
12
+ BaseEvent,
13
+ ImageEvent,
14
+ LogEvent,
15
+ ObserverEvent,
16
+ SplitEvent,
17
+ StepEvent,
18
+ )
19
+
20
+ __all__ = [
21
+ "ActionEvent",
22
+ "BaseEvent",
23
+ "ImageEvent",
24
+ "LogEvent",
25
+ "ObserverEvent",
26
+ "SplitEvent",
27
+ "StepEvent",
28
+ ]